#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <modbus.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "cJSON.h"
#include "msg_queue_peer.h"
#include "shmem.h"
#include "list.h"
#define MODBUS_IP "192.168.50.29"
#define MODBUS_PORT 502
union val_t
{
    int b_val;   // bool类型存储空间
    int i_val;   // 整形值存储空间
    float f_val; // 浮点值存储空间
};
struct que_order // 消息队列的消息类型
{
    long tag;       // 消息队列的消息类型
    int key;        // 数据节点唯一标识
    char val[32];   // 数据
};
struct data_node
{
    int key;             // 唯一键值
    int type;            // 数据点类型
    int dev_type;        // 数据点属于哪个设备，根据网关支持的设备自行定义
    union val_t old_val; // 变化上报后需要更新旧值
    union val_t new_val; // 从共享内存取出最新数据，放到new_val中
    int ret;             // 默认为-1，采集成功后设置为0，采集失败再置-1
};
struct addre
{
    int addr;
    int key;
};
struct data
{
    struct addre node;
    struct list_head list;
};
modbus_t *ctx;
// 解析点表的addr
int list(int key)
{
    // 获取链表解析
    int fd = 0;
    char buff[2048];
    fd = open("/mnt/config/five.json", O_RDWR);
    if (fd < 0)
    {
        perror("open err\n");
    }
    // 将文件内容读取到数组里
    if (read(fd, buff, 2048) < 0)
    {
        printf("read is not finish %d", __LINE__);
    }
    // 关闭文件描述符
    close(fd);
    // 反序列化
    cJSON *root = cJSON_Parse(buff);
    cJSON *mod = cJSON_GetObjectItem(root, "modbus");
    cJSON *modbus = cJSON_GetObjectItem(mod, "data");
    int n = cJSON_GetArraySize(modbus);
    // 内核链表操作
    struct list_head head;
    INIT_LIST_HEAD(&head);
    for (int i = 0; i < n; i++)
    {
        // 获取数组节点中第一个数组节点的指针
        cJSON *data = cJSON_GetArrayItem(modbus, i);
        // 数组节点中名字叫key addr的节点
        cJSON *key = cJSON_GetObjectItem(data, "key");
        cJSON *addr = cJSON_GetObjectItem(data, "addr");
        struct data *da = malloc(sizeof(struct data)); // 开辟空间下面内核才能遍历
        da->node.key = key->valueint;
        da->node.addr = addr->valueint;
        list_add(&da->list, &head); // 添加至内核链表
        // printf("%d:%d\n", da->node.key, da->node.addr); //用于打印key和addr值
    }
    struct list_head *pos;
    struct data *tmp;
    list_for_each(pos, &head)
    {
        tmp = list_entry(pos, struct data, list);
        // printf("%d\n", tmp->node.key); //查看是否从尾往头循环遍历
        if (key == tmp->node.key)
        {
            return tmp->node.addr;
            break;
        }
    }
}
// modbus读取设备线程
void *modbus_thread(void *arg)
{

    // 初始化创建共享内存
    struct shm_param para;
    if (shm_init(&para, "five", 1024))
    {
        perror("shm_init err\n");
    }
    int *num = shm_getaddr(&para); // 指针指向共享内存的地址->映射
    if (NULL == num)
    {
        perror("shm getaddr err");
    }
    struct data_node *mb;
    mb = (struct data_node *)num + 1; // 往下加1的地址存放数据
    uint16_t buf[1024] = {};
    uint16_t dataa[32] = {};
    uint8_t data[132] = {};
    uint8_t data1[32] = {};
    uint8_t data2[32] = {};
    uint8_t data3[32] = {};
    uint8_t data4[32] = {};
    while (1) // 循环检测modbus salve里线圈和寄存器信息
    {
        sleep(2);
        // 测试是否能正常运行 给定数据
        //  printf("%d\n",list(101));
        // modbus_read_bits(ctx, list(100), 1, data4);
        //  modbus_read_bits(ctx, list(103), 1, data1);
        //  modbus_read_bits(ctx, list(105), 1, data3);
       //printf("%d\n",*num); //查看共享内存的num值
        for (int i = 0; i < *num; i++) // 从程序上报那里得到num值 循环遍历结构体 向共享空间补充数据
        {
            if ((mb + i)->key == 101) // 空调开关
            {
                modbus_read_bits(ctx, list(101), 1, data4);
                (mb + i)->new_val.i_val = data4[0];
            }
           else  if ((mb + i)->key == 102) // 空调温度
            {
                modbus_read_registers(ctx, list(102), 1, dataa);
                (mb + i)->new_val.f_val = dataa[0];
            }
           else  if ((mb + i)->key == 103) // 制冷开关
            {
                modbus_read_bits(ctx, list(103), 1, data);
                (mb + i)->new_val.i_val = data[0];
            }
           else  if ((mb + i)->key == 104) // 制热开关
            {
                modbus_read_bits(ctx, list(104), 1, data1);
                (mb + i)->new_val.i_val = data1[0];
            }
           else  if ((mb + i)->key == 105) // 窗帘开关
            {
                modbus_read_bits(ctx, list(105), 1, data2);
                (mb + i)->new_val.i_val = data2[0];
            }
            else if ((mb + i)->key == 106) // 投影仪开关
            {
                modbus_read_bits(ctx, list(106), 1, data3);
                (mb + i)->new_val.i_val = data3[0];
            }
        }
        printf("空调开关：%d 空调温度：%d 制冷开关：%d 制热开关 :%d 窗帘开关 :%d 关 投影屏开关 :%d\n", data4[0], dataa[0], data[0], data1[0], data2[0], data3[0]);

        memset(dataa, 0, sizeof(dataa));
        memset(data, 0, sizeof(data));
    }
    shm_del(&para);
    pthread_exit(NULL);
}
// modbus写入线圈
void *modbus_write(void *arg)
{
    struct que_order msg1;
    while (1)
    {
        // 用消息队列接收指令
        msg_queue_recv("five", &msg1, sizeof(struct que_order), 1, 0);
        if (msg1.key == 101)
        {
            modbus_write_bit(ctx, list(101), atoi(msg1.val)); // 制冷开关
        }
        if (msg1.key == 102)
        {
            modbus_write_register(ctx, list(102), atoi(msg1.val)); // 空调温度设置
        }
        if (msg1.key == 103)
        {
            modbus_write_bit(ctx, list(103), atoi(msg1.val)); // 制冷开关
        }
        if (msg1.key == 104)
        {
            modbus_write_bit(ctx, list(104), atoi(msg1.val)); // 制热开关
        }
        if (msg1.key == 105)
        {
            modbus_write_bit(ctx, list(105), atoi(msg1.val)); // 窗帘开关
        }
        if (msg1.key == 106)
        {
            modbus_write_bit(ctx, list(106), atoi(msg1.val)); // 投影仪开关
        }
    }
    pthread_exit(NULL);
    // pause();
}
int main(int argc, char const *argv[])
{
    ctx = modbus_new_tcp(MODBUS_IP, MODBUS_PORT);
    if (ctx == NULL)
    {
        perror("modbus create err");
    }
    if (modbus_set_slave(ctx, 1) != 0)
    {
        perror("set id err");
    }
    if (modbus_connect(ctx) != 0)
    {
        perror("connect err");
    }
    pthread_t tid, tid1, tid2;
    pthread_create(&tid, NULL, modbus_thread, NULL);
    pthread_create(&tid1, NULL, modbus_write, NULL);
    pthread_detach(tid);
    pthread_detach(tid1);
    while (1)
        ;
    modbus_free(ctx);
    modbus_close(ctx);
    return 0;
}